<?php

/**
 * Базовый класс работы с БД
 *
 */
includeClass('SqlProcessor', __DIR__ . "/classes");

define('DB_COND_NOT_BETWEEN', -5);
define('DB_COND_NOT_IN', -4);
define('DB_COND_NOT_LIKE', -3);
define('DB_COND_LT', -2);
define('DB_COND_LTE', -1);
define('DB_COND_EQUAL', 0);
define('DB_COND_GTE', 1);
define('DB_COND_GT', 2);
define('DB_COND_LIKE', 3);
define('DB_COND_IN', 4);
define('DB_COND_BETWEEN', 5);

define('DB_LOGIC_NOT', -1);
define('DB_LOGIC_AND', 0);
define('DB_LOGIC_OR', 1);

class Database {

    protected $px, $data, $action; //префикс таблиц, данные конструктора, последнее индексируемое действия

    /**
     * 
     * @param mixed $params массив параметров | [NULL]
     * @return \Database Экземпляр БД
     */

    function __construct($params) {
        $this->px = isset($params['px']) ? $params['px'] : "";
        return $this;
    }

    /*
     * Запись в конструктор действия SELECT
     */

    function select() {
        $this->data['action'] = __FUNCTION__;
        return $this;
    }

    /*
     * Запись в конструктор действия INSERT
     */

    function insert() {
        $this->data['action'] = __FUNCTION__;
        return $this;
    }

    /*
     * Запись в конструктор действия UPDATE
     */

    function update() {
        $this->data['action'] = __FUNCTION__;
        return $this;
    }

    /*
     * Запись в конструктор действия DELETE
     */

    function delete() {
        $this->data['action'] = __FUNCTION__;
        return $this;
    }

    /**
     * Запись в конструктор действия чистого SQL
     * и самого SQL-скрипта
     * 
     * @param str $query Запрос SQL
     * @return \Database текущее состояние экземпляра
     * 
     */
    function native($query) {
        $this->data['action'] = __FUNCTION__;
        $this->data['query'] = preg_replace("/@px:/", $this->px, $query);
        return $this;
    }

    /**
     * Запись в данные конструктора параметра options
     * 
     * @param str $str Опции
     * @return \Database текущее состояние экземпляра
     */
    function option($str) {
        $this->data[__FUNCTION__] = $str;
        return $this;
    }

    /**
     * Запись в данные конструктора параметра tables
     * 
     * @param mixed $tables Таблицы array|str. При записи нескольких таблиц в виде масива использовать конструкцию типа 'имя_таблицы' => 'алиас' или 'имя_таблицы'
     * @param mixed $alias алиас таблицы. Используется при записи одной таблицы
     * @return \Database
     */
    function table($tables, $alias = false) {
        if (is_array($tables)) {
            foreach ($tables as $table => $as) {
                if (is_numeric($table)) {
                    $table = $as;
                    $as = false;
                }
                $this->data[__FUNCTION__][] = array('name' => $this->px . $table, 'alias' => $as);
            }
        } else {
            $this->data[__FUNCTION__][] = array('name' => $this->px . $tables, 'alias' => $alias);
        }
        return $this;
    }

    /**
     * Запись в данные конструктора параметра field
     * 
     * @param mixed $field Таблицы array|str. При записи нескольких полей в виде масива использовать конструкцию типа 'имя_поля' => 'алиас' или 'имя_поля'
     * @param mixed $alias алиас поля. Используется при записи однго поля
     * @return \Database
     */
    function field($tables = "*", $alias = false) {
        if (is_array($tables)) {
            foreach ($tables as $table => $as) {
                if (is_numeric($table)) {
                    $table = $as;
                    $as = false;
                }
                $this->data[__FUNCTION__][] = array('name' => $table, 'alias' => $as);
            }
        } else {
            $this->data[__FUNCTION__][] = array('name' => $tables, 'alias' => $alias);
        }
        return $this;
    }

    /**
     * Метод сортировки 
     * 
     * @param str $field поле сортировки
     * @param str $direction направление сортировки
     * @return \Database
     */
    function order($field, $direction) {
        $this->data[__FUNCTION__][] = array(
            'field' => $field,
            'direction' => $direction,
        );
        return $this;
    }

    /**
     * Лимит выбора
     * 
     * @param int $from индекс первого элемента
     * @param type $length количество записей
     * @return \Database
     */
    function limit($from, $length) {
        $this->data[__FUNCTION__] = array(
            'from' => $from,
            'length' => $length,
        );
        return $this;
    }

    /**
     * Вставляемые значения
     * 
     * @param array $values массив значений, соответствующий полям таблицы
     * @return \Database
     */
    function values($values) {
        $data = func_get_args();
        foreach ($data as $value) {
            $this->data[__FUNCTION__][] = $value;
        }
        return $this;
    }

    /**
     * Условие проверки
     * 
     * @param str $filed Поле
     * @param str $value Значение
     * @param const $cond Тип сравнения (по-умолчанию - "=")
     * @param const $logic тип логической связи (по-умолчанию - "AND")
     * @return \Database
     */
    function cond($filed, $value, $cond = DB_COND_EQUAL, $logic = DB_LOGIC_AND) {
        $this->data[__FUNCTION__][] = array(
            'field' => $filed,
            'value' => $value,
            'cond' => $cond,
            'logic' => $logic,
            'noquotes' => false,
        );
        $this->action = __FUNCTION__;
        return $this;
    }

    /**
     * Обновить значение поля
     * @param str $filed Поле
     * @param mixed $value Значение
     * @return \Database
     */
    function set($filed, $value) {
        $this->data[__FUNCTION__][] = array(
            'field' => $filed,
            'value' => $value,
            'noquotes' => false,
        );
        $this->action = __FUNCTION__;
        return $this;
    }

    /**
     * Выполнить запрос
     */
    function execute() {
        Logger::message($this->getSql());
        $this->DropConstructor();
    }

    /**
     * Убрать кавычки с последнего значения индексированного действия
     * @return \Database
     */
    function noquotes() {
        $this->data[$this->action][count($this->data[$this->action]) - 1]['noquotes'] = true;
        return $this;
    }

    /**
     * Получить стрку SQL-запроса
     * @return type
     */
    function getSql() {
        $str = "@action:";
        $SQLPROC = new SqlProcessor;
        foreach ($this->data as $method => $params) {
            $str = preg_replace("/@$method:/si", $SQLPROC->$method($params), $str);
        }
        $this->DropConstructor();
        $str = preg_replace("/\s@[\S]+:/si", "", $str);
        $SQLPROC = null;
        return $str;
    }

    /**
     * Подключение класса выбранной БД
     * @param array $params массив параметров
     * @return \class|\Database Экземпляр класса выбранной БД
     */
    static function Init($params = array()) {
        if (empty($params)) {
            $params = parse_ini_file(__DIR__ . "/config.ini");
        }
        if (isset($params['type']) && $params['type'] != "" && $params['type'] != "Database") {
            $filename = __DIR__ . "/classes/" . $params['type'] . ".class.php";
            if (file_exists($filename)) {
                $class = "\\Database\\" . $params['type'];
                require_once $filename;
                return new $class($params);
            } else {
                return new Database($params);
            }
        } else {
            return new Database($params);
        }
    }

    /**
     * Получить поля таблицы
     * 
     * @param str $tablename 
     * @return array массив полей
     */
    function GetFields($tablename) {
        return array();
    }

    /**
     * Сброс конструктора
     */
    function DropConstructor() {
        $this->data = null;
        $this->action = null;
    }

}
